/*
 * Copyright (C) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "cache/file_cache.h"
#include "cache/memory_cache.h"
#include "common/napi_helper.h"
#include "common/number_constant.h"
#include "controller/lottie_controller_bridge.h"
#include "controller/lottie_handler.h"
#include "dataManager/lottie_config_data.h"
#include "hilog/log.h"
#include "napi/native_api.h"
#include "node/lottie_manager.h"
#include "node/lottie_xcomponent_node.h"
#include "rawfile/raw_file_manager.h"
#include "task_pool/task_pool.h"
#include <arkui/native_node_napi.h>
#include <cstdint>
#include <regex>
/*
    ets侧调用接口
*/
using namespace LottieTurbo;
static napi_value LottieBasicAction(napi_env env, napi_callback_info info,
                                    std::function<void(LottieHandler *, double value)> basicActionFunc,
                                    bool requiresValue = false)
{
    NapiHelper helper(env);
    auto args = helper.GetCallbackArgs(info);
    auto &lottieManager = LottieManager::GetInstance();
    auto handleList = lottieManager.GetLottieHandlerList();

    // 如果需要处理一个 double 值，先获取该值
    double value = requiresValue ? helper.GetDouble(args[0]) : 0.0;

    // 确定参数索引
    size_t expectedArgsSize = requiresValue ? 1 : 0;
    size_t nameIndex = requiresValue ? 1 : 0;

    if (args.size() == expectedArgsSize || helper.CheckIsType(args[nameIndex], napi_null) ||
        helper.CheckIsType(args[nameIndex], napi_undefined)) {
        for (auto &lottieHandler : handleList) {
            if (lottieHandler != nullptr) {
                basicActionFunc(lottieHandler.get(), value);
            }
        }
        return nullptr;
    }

    std::string name = helper.GetString(args[nameIndex]);
    auto lottieHandler = lottieManager.GetHandleById(name);
    if (lottieHandler != nullptr) {
        basicActionFunc(lottieHandler.get(), value);
    } else {
        DRAWING_LOGE("LottieBasicAction: Lottie handler for %{public}s is nullptr.", name.c_str());
    }
    return nullptr;
}

static napi_value CreateNativeRoot(napi_env env, napi_callback_info info)
{
    NapiHelper helper(env);
    auto args = helper.GetCallbackArgs(info);
    auto contentHandle = helper.GetNodeContentHandle(args[NUM_0]);
    auto xcomponentId = helper.GetString(args[NUM_1]);
    bool repeat = LottieManager::GetInstance().RepeatLottieId(xcomponentId);
    if (repeat) {
        return nullptr;
    }
    napi_ref *contentRef = new napi_ref;
    napi_create_reference(env, args[NUM_0], 1, contentRef);
    auto config = std::make_shared<LottieConfigBridge>(env, args[NUM_2]);
    auto node = std::make_shared<LottieXcomponentNode>(xcomponentId, config->GetRadius(), config->GetbgColor());
    auto handler = std::make_shared<LottieHandler>(xcomponentId, config);
    handler->lottieListener_ = std::move(config->GetLottieListener());
    handler->safeFunc_ = std::move(config->GetSafeFunction());
    bool ret = LottieManager::GetInstance().AddLottieNode(xcomponentId, contentHandle, node, handler);
    if (ret == false) {
        return nullptr;
    }
    auto task = [](std::shared_ptr<LottieHandler> handler, std::shared_ptr<LottieXcomponentNode> node) {
        if (handler && node) {
            node->SetHandler(handler);
            node->SetAnimator();
            HITRACE("Lottie::InitHandleData", handler->InitHandleData());
            HITRACE("Lottie::NodeInit", node->Init());
        }
    };
    TaskPool::GetInstance().PushTask(ffrt_queue_priority_high, task, handler, node);
    return nullptr;
}

static napi_value lottiePlay(napi_env env, napi_callback_info info)
{
    return LottieBasicAction(env, info, [](LottieHandler *handler, double value) { handler->Play(); });
}

static napi_value lottiePause(napi_env env, napi_callback_info info)
{
    return LottieBasicAction(env, info, [](LottieHandler *handler, double value) { handler->Pause(); });
}

static napi_value lottieStop(napi_env env, napi_callback_info info)
{
    return LottieBasicAction(env, info, [](LottieHandler *handler, double value) { handler->Stop(); });
}

static napi_value lottieSetSpeed(napi_env env, napi_callback_info info)
{
    return LottieBasicAction(
        env, info, [](LottieHandler *handler, double speed) { handler->SetSpeed(speed); }, true);
}

static napi_value lottieSetDirection(napi_env env, napi_callback_info info)
{
    return LottieBasicAction(
        env, info, [](LottieHandler *handler, double direction) { handler->SetDirection(direction); }, true);
}

static napi_value lottieTogglePause(napi_env env, napi_callback_info info)
{
    return LottieBasicAction(env, info, [](LottieHandler *handler, double value) { handler->TogglePause(); });
}

static void HandleDisposeLottieNode(std::string id)
{
    auto func = [](std::string id) {
        LottieManager::GetInstance().DisposeLottieNode(id);
    };
    auto trigger = new LottieHandler::TriggerData;
    trigger->func = std::bind(func, id);
    auto handler = LottieManager::GetInstance().GetHandleById(id);
    if (handler == nullptr) {
        return;
    }
    napi_call_threadsafe_function(handler->safeFunc_, trigger, napi_tsfn_blocking);
}

static napi_value lottieDestroy(napi_env env, napi_callback_info info)
{
    NapiHelper helper(env);
    auto args = helper.GetCallbackArgs(info);
    if (args.size() != 1) {
        return nullptr;
    }
    if (helper.CheckIsType(args[NUM_0], napi_undefined)) {
        auto handleList = LottieManager::GetInstance().GetLottieHandlerList();
        for (auto &lottieHandler : handleList) {
            lottieHandler->isDestroy_ = true;
            TaskPoolLocal::GetInstance().PushTask(HandleDisposeLottieNode, lottieHandler->GetLottieId());
        }
    } else {
        std::string lottieId = helper.GetString(args[NUM_0]);
        auto handler = LottieManager::GetInstance().GetHandleById(lottieId);
        if (handler == nullptr) {
            return nullptr;
        }
        handler->isDestroy_ = true;
        TaskPoolLocal::GetInstance().PushTask(HandleDisposeLottieNode, lottieId);
    }
    return nullptr;
}

static napi_value lottieSetFrameRate(napi_env env, napi_callback_info info)
{
    auto basicActionFunc = [](LottieHandler *handler, double frameRate) {
        float maxFrameRate = handler->GetLottieFileTotalFrameRate();
        ArkUI_ExpectedFrameRateRange expectFrameRateRange;
        constexpr float frameRateLimits = 120;
        // 区间(1-----120)
        // 1. 设置帧率 = 0; 解除区间限制; 实际帧率
        // 2. 设置帧率 < 1; 区间(1-----120); frameRate = 1
        // 3. 1 < 实际帧率 < 设置帧率 < 120; 限制为源文件帧率
        // 4. 1 <= 设置帧率 <= 实际帧率 < 120;
        //      此时不需要修改，因为 frameRate 已经是用户指定的值
        // 5. 实际帧率 < 120 <= 设置帧率; 限制为源文件帧率
        // 6. 120 <= 实际帧率 <= 设置帧率 / 120 < 设置帧率 < 实际帧率; frameRate = 120
        if (frameRate == 0 || (maxFrameRate < frameRate && frameRate < frameRateLimits) ||
            (maxFrameRate < frameRateLimits && frameRate >= frameRateLimits)) {
            // 1,3,5条件
            frameRate = maxFrameRate;
        } else if (frameRate <= 0) {
            // 2条件
            return;
        } else if (frameRate >= frameRateLimits && maxFrameRate >= frameRateLimits) {
            // 6条件
            frameRate = frameRateLimits;
        }
        expectFrameRateRange = {.min = static_cast<uint32_t>(frameRate),
                                .max = static_cast<uint32_t>(frameRate),
                                .expected = static_cast<uint32_t>(frameRate)};
        DRAWING_LOGD("lottieSetFrameRate::Final maxFrameRate : %{public}f, frameRate : %{public}f.", maxFrameRate,
                     frameRate);
        handler->Pause();                                            // 暂停动画
        handler->SetFrameRate(frameRate);                            // 设置动画源帧率
        handler->animator_->SetFrameRateRange(expectFrameRateRange); // 设置帧率范围
        handler->animator_->UpdateFrameRateRange();                  // 更新帧率范围
        handler->Play();                                             // 播放动画
    };
    return LottieBasicAction(env, info, basicActionFunc, true);
}

static napi_value LottieInitFileCache(napi_env env, napi_callback_info info)
{
    FileCache::GetInstance()->InitFileCache();
    return nullptr;
}

static napi_value LottieResizeFileCache(napi_env env, napi_callback_info info)
{
    std::vector<napi_value> vec;
    NapiHelper napiHelper(env);
    vec = napiHelper.GetCallbackArgs(info);
    size_t number = napiHelper.GetInteger(vec[NUM_0]);
    int64_t capacity = napiHelper.GetInteger(vec[NUM_1]);
    FileCache::GetInstance()->SetCacheSize(number, capacity);
    return nullptr;
}

static napi_value lottieClearFileCache(napi_env env, napi_callback_info info)
{
    std::vector<napi_value> vec;
    NapiHelper napiHelper(env);
    vec = napiHelper.GetCallbackArgs(info);
    if (vec.size() == 0) {
        FileCache::GetInstance()->RemoveAllFile();
    } else {
        std::string url = napiHelper.GetString(vec[NUM_0]);
        FileCache::GetInstance()->RemoveFile(url);
    }
    return nullptr;
}

static napi_value lottieResizeCache(napi_env env, napi_callback_info info)
{
    std::vector<napi_value> vec;
    NapiHelper napiHelper(env);
    vec = napiHelper.GetCallbackArgs(info);
    size_t number = napiHelper.GetInteger(vec[NUM_0]);
    int64_t capacity = napiHelper.GetInteger(vec[NUM_1]);
    MemoryCache::GetInstance()->SetMemoryCache(number, capacity);
    return nullptr;
}

static napi_value lottieClearCache(napi_env env, napi_callback_info info)
{
    MemoryCache::GetInstance()->RemoveAll();
    return nullptr;
}

static napi_value lottieRemoveCache(napi_env env, napi_callback_info info)
{
    std::vector<napi_value> vec;
    NapiHelper napiHelper(env);
    vec = napiHelper.GetCallbackArgs(info);
    if (vec.size() == NUM_1) {
        std::string key = napiHelper.GetString(vec[NUM_0]);
        MemoryCache::GetInstance()->Remove(key);
    }
    return nullptr;
}

static napi_value lottieContainsCache(napi_env env, napi_callback_info info)
{
    std::vector<napi_value> vec;
    NapiHelper napiHelper(env);
    napi_value result = napiHelper.CreateBoolean(false);
    vec = napiHelper.GetCallbackArgs(info);
    if (vec.size() == NUM_1) {
        std::string key = napiHelper.GetString(vec[NUM_0]);
        auto data = MemoryCache::GetInstance()->GetData(key);
        if (data == nullptr) {
            return result;
        }
        return napiHelper.CreateBoolean(true);
    }
    return result;
}

static napi_value ConfigureCacheSize(napi_env env, napi_callback_info info)
{
    NapiHelper helper(env);

    auto args = helper.GetCallbackArgs(info, NUM_1);
    if (args.empty()) {
        napi_throw_error(env, nullptr, "Wrong number of arguments");
        return nullptr;
    }

    if (!helper.CheckIsType(args[NUM_0], napi_number)) {
        return nullptr;
    }

    int cacheSize = helper.GetInteger(args[NUM_0]);

    // 调用底层接口
    rlottie::configureModelCacheSize(cacheSize);
    OH_LOG_Print(LOG_APP, LOG_DEBUG, LOG_DOMAIN, "ConfigureCacheSize", "cacheSize = %{public}d", cacheSize);
    return nullptr;
}

static napi_value LottieSetVisible(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto args = napiHelper.GetCallbackArgs(info);
    if (args.size() != NUM_2) {
        return nullptr;
    }
    auto lottieId = napiHelper.GetString(args[NUM_0]);
    auto visible = napiHelper.GetBoolean(args[NUM_1]);
    auto lottieHandler = LottieManager::GetInstance().GetHandleById(lottieId);
    if (lottieHandler != nullptr) {
        auto state = lottieHandler->GetPlayState();
        if (state == LottieHandler::PlayState::PLAY && visible == false) {
            lottieHandler->SetPlayState(LottieHandler::PlayState::INVISIBLE_PAUSE);
            lottieHandler->Pause();
        }
        if (state == LottieHandler::PlayState::INVISIBLE_PAUSE && visible == true) {
            lottieHandler->SetPlayState(LottieHandler::PlayState::PLAY);
            lottieHandler->Play();
        }
        lottieHandler->dataManager_->SetVisible(visible);
        OH_LOG_Print(LOG_APP, LOG_DEBUG, LOG_DOMAIN, "LottieSetVisible",
                     "LottieId = %{public}s, Visible = %{public}d", lottieId.c_str(), visible);
    }
    return nullptr;
}

static napi_value RenderToImage(napi_env env, napi_callback_info info)
{
    NapiHelper napiHelper(env);
    auto args = napiHelper.GetCallbackArgs(info);
    if (args.size() < NUM_4 || args.size() > NUM_5) {
        return nullptr;
    }
    std::shared_ptr<DataManager> dataManager = std::make_shared<DataManager>();
    if (args.size() == NUM_5) {
        auto resManager = OH_ResourceManager_InitNativeResourceManager(env, args[NUM_4]);
        dataManager->mNativeResMgr_ =
            std::shared_ptr<NativeResourceManager>(resManager, OH_ResourceManager_ReleaseNativeResourceManager);
        auto resource = napiHelper.GetResource(args[NUM_0]);
        dataManager->InitFromFile(resource);
    } else if (napiHelper.CheckIsType(args[NUM_0], napi_string)) {
        auto lottie = napiHelper.GetString(args[NUM_0]);
        dataManager->InitFromJson(lottie);
        if (dataManager->player == nullptr) {
            dataManager->InitFromFile(lottie);
        }
    }
    if (dataManager->player == nullptr) {
        DRAWING_LOGE("Lottie::RenderToImage, dataManager->player Null.");
        return nullptr;
    }
    auto frameNum = napiHelper.GetDouble(args[NUM_1]);
    auto width = napiHelper.GetDouble(args[NUM_2]);
    auto height = napiHelper.GetDouble(args[NUM_3]);
    auto renderTree = dataManager->player->renderTree(frameNum, width, height);
    if (renderTree == nullptr) {
        return nullptr;
    }
    std::shared_ptr<LottieRender> lottieRender = std::make_shared<LottieRender>();
    napi_value result;
    void *bitmap = nullptr;
    napi_status status = napi_create_arraybuffer(env, width * height * RGBANUM, &bitmap, &result);
    if (status != napi_ok) {
        return nullptr;
    }
    if (bitmap == nullptr) {
        return nullptr;
    }
    if (lottieRender->RenderToImage(renderTree, width, height, bitmap)) {
        DRAWING_LOGD("Lottie::RenderToImage, RenderTree width:%{public}f,height:%{public}f", width, height);
        return result;
    }
    return nullptr;
}

static napi_value LottieClearTmpResource(napi_env env, napi_callback_info info)
{
    // 检查是否存在动画句柄,如果存在则有可能正在使用tmp中的资源，这里不进行清除临时文件，避免异常
    if (LottieManager::GetInstance().GetLottieHandlerList().size() == 0) {
        FileCache::GetInstance()->ClearTmpFile();
    }
    return nullptr;
}

EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
    DRAWING_LOGI("LottieTurbo Version: %{public}s", VERSION);
    napi_property_descriptor desc[] = {
        {"lottieClearTmpResource", nullptr, LottieClearTmpResource, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"lottiePlay", nullptr, lottiePlay, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"lottiePause", nullptr, lottiePause, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"lottieStop", nullptr, lottieStop, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"lottieSetSpeed", nullptr, lottieSetSpeed, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"lottieSetDirection", nullptr, lottieSetDirection, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"lottieTogglePause", nullptr, lottieTogglePause, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"lottieDestroy", nullptr, lottieDestroy, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"lottieSetFrameRate", nullptr, lottieSetFrameRate, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"lottieInitFileCache", nullptr, LottieInitFileCache, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"lottieResizeFileCache", nullptr, LottieResizeFileCache, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"lottieClearFileCache", nullptr, lottieClearFileCache, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"lottieResizeCache", nullptr, lottieResizeCache, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"lottieClearCache", nullptr, lottieClearCache, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"lottieRemoveCache", nullptr, lottieRemoveCache, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"lottieContainsCache", nullptr, lottieContainsCache, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"createNativeRoot", nullptr, CreateNativeRoot, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"configureCacheSize", nullptr, ConfigureCacheSize, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"lottieSetVisible", nullptr, LottieSetVisible, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"renderToImage", nullptr, RenderToImage, nullptr, nullptr, nullptr, napi_default, nullptr}};
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    LottieControllerBridge::Exports(env, exports);
    FileCache::GetInstance()->InitFileCache();
    return exports;
}
EXTERN_C_END

static napi_module LottieTurboModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "lottie-turbo",
    .nm_priv = ((void *)0),
    .reserved = {0},
};

extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
{
    napi_module_register(&LottieTurboModule);
}
